<template>
    <div class="page_root" :key="queryUpdateFlag" data-page-name="1" data-page-identification="x_hb0xe6o0k2">
        <a-spin :spinning="pageLoading.loading" :tip="pageLoading.tip">
            <!-- begin vid_q2pj193dfc 自定义模板_7xu4mdl9mj -->
            <!--          新建编辑工单SOP-->
            <a-modal
                :visible="sopState.sopModalVisible"
                :title="sopState.modalTitle"
                @ok="confirmSop"
                @cancel="cancelSop"
                centered
                destroy-on-close
                :ok-button-props="{ loading: sopState.okLoading }"
                width="100%"
                :mask-closable="false"
                :body-style="{
                    maxHeight: '800px',
                    overflowY: 'auto',
                    paddingBottom: 0
                }"
                :footer="sopState.isDetail ? null : undefined"
            >
                <a-form :model="sopState.sopModalForm" ref="sopFormRef" :rules="sopState.sopFormRole">
                    <div class="sop-modal">
                        <a-row>
                            <a-col :span="24">
                                <div :class="['sop-modal', { 'readonly-wrapper': sopState.isDetail }]">
                                    <a-col :span="24">
                                        <a-form-item
                                            label="SOP名称"
                                            name="sopName"
                                            :rules="sopState.sopFormRole.sopName"
                                        >
                                            <a-input
                                                v-model:value="sopState.sopModalForm.sopName"
                                                placeholder="请输入"
                                                maxlength="50"
                                                show-count
                                            />
                                        </a-form-item>
                                    </a-col>
                                    <a-row>
                                        <a-col :span="8">
                                            <a-form-item
                                                label="工单类型"
                                                :rules="sopState.sopFormRole.workType"
                                                name="workType"
                                            >
                                                <a-select
                                                    :options="sopState.workTypeList"
                                                    v-model:value="sopState.sopModalForm.workType"
                                                    :field-names="{ label: 'label', value: 'value' }"
                                                    placeholder="请选择"
                                                    @change="hanldeTypeChange(false)"
                                                    :filterOption="
                                                        (input, option) => {
                                                            return (
                                                                option.label
                                                                    .toLowerCase()
                                                                    .indexOf(input.toLowerCase()) >= 0
                                                            )
                                                        }
                                                    "
                                                />
                                            </a-form-item>
                                        </a-col>
                                        <a-col :span="8">
                                            <a-form-item
                                                label="监控场景"
                                                :rules="sopState.sopFormRole.monitorScene"
                                                name="monitorScene"
                                            >
                                                <a-select
                                                    @focus="getMonitorSceneList(false)"
                                                    :options="sopState.monitorSceneList"
                                                    v-model:value="sopState.sopModalForm.monitorScene"
                                                    placeholder="请选择"
                                                    show-search
                                                    :field-names="{ label: 'label', value: 'value' }"
                                                    :filterOption="
                                                        (input, option) => {
                                                            return (
                                                                option.label
                                                                    .toLowerCase()
                                                                    .indexOf(input.toLowerCase()) >= 0
                                                            )
                                                        }
                                                    "
                                                />
                                            </a-form-item>
                                        </a-col>
                                        <a-col :span="8">
                                            <a-form-item label="所属业务">
                                                <a-select
                                                    :options="[
                                                        {
                                                            label: '工单',
                                                            value: '工单'
                                                        }
                                                    ]"
                                                    v-model:value="sopState.sopModalForm.belongBusiness"
                                                    placeholder="请选择"
                                                    show-search
                                                    :field-names="{ label: 'label', value: 'value' }"
                                                    :filterOption="
                                                        (input, option) => {
                                                            return (
                                                                option.label
                                                                    .toLowerCase()
                                                                    .indexOf(input.toLowerCase()) >= 0
                                                            )
                                                        }
                                                    "
                                                />
                                            </a-form-item>
                                        </a-col>
                                    </a-row>
                                </div>
                            </a-col>

                            <a-col :span="24">
                                <div class="modal-sop-title">
                                    <div>SOP步骤配置：</div>
                                    <div
                                        ><a-button size="small" type="primary" @click="resetNodes"
                                            >重置步骤</a-button
                                        ></div
                                    >
                                </div>
                            </a-col>
                            <a-col :span="24">
                                <div class="flow-content">
                                    <!-- 主体部分：左中右 -->
                                    <div class="flow-container">
                                        <div class="sidebar">
                                            <div
                                                v-for="node in presetNodes"
                                                :key="node.type"
                                                class="draggable-node"
                                                :draggable="true"
                                                @dragstart="onDragStart($event, node)"
                                            >
                                                {{ node.data.label }}
                                            </div>
                                        </div>
                                        <!-- 画布 -->
                                        <div class="flow-area" @drop="onDrop" @dragover="onDragOver">
                                            <VueFlow
                                                v-model:nodes="nodes"
                                                v-model:edges="edges"
                                                @node-click="onNodeClick"
                                                @connect="onConnect"
                                                :selectEdgesOnClick="true"
                                            >
                                                <template #edge-custom="props">
                                                    <BaseEdge
                                                        :id="props.id"
                                                        :path="
                                                            getStraightPath({
                                                                sourceX: props.sourceX,
                                                                sourceY: props.sourceY,
                                                                targetX: props.targetX,
                                                                targetY: props.targetY
                                                            })[0]
                                                        "
                                                        :label-x="(props.sourceX + props.targetX) / 2"
                                                        :label-y="(props.sourceY + props.targetY) / 2"
                                                        label-bg-style="fill: whitesmoke"
                                                    />
                                                    <foreignObject
                                                        :x="(props.sourceX + props.targetX) / 2 - 10"
                                                        :y="(props.sourceY + props.targetY) / 2 - 10"
                                                        width="20"
                                                        height="20"
                                                        class="edge-delete-fo"
                                                    >
                                                        <div
                                                            class="edge-delete-btn"
                                                            v-if="props.selected"
                                                            @click.stop="removeEdge(props.id)"
                                                        >
                                                            x
                                                        </div>
                                                    </foreignObject>
                                                </template>

                                                <!--                        自定义节点-->
                                                <template #node-start="nodeProps">
                                                    <div class="custom-node">
                                                        <div class="handle">
                                                            <handle type="source" position="right" />
                                                        </div>
                                                        <div class="node-content">
                                                            {{ nodeProps.data.label }}
                                                        </div>
                                                    </div>
                                                </template>
                                                <template #node-input="nodeProps">
                                                    <div
                                                        class="custom-node"
                                                        :style="{
                                                            border: getBacColor(nodeProps),
                                                            color: getFontColor(nodeProps)
                                                        }"
                                                    >
                                                        <handle type="target" position="left" />
                                                        <div class="node-line" :title="nodeProps.data.sopStep">
<!--                                                            <div>-->
<!--                                                                步骤名称：{{-->
<!--                                                                    nodeProps.data.sopStep || '点击编辑SOP步骤'-->
<!--                                                                }}</div-->
<!--                                                            >-->
<!--                                                            <div> 解决角色：{{ nodeProps.data.solverRole }}</div>-->
                                                            {{
                                                                nodeProps.data.sopStep || '点击编辑SOP步骤'
                                                            }}
                                                        </div>
                                                        <handle type="source" position="right" />
                                                        <div
                                                            class="node-delete-btn"
                                                            v-if="nodeProps.selected"
                                                            @click.stop="removeNode(nodeProps.id)"
                                                        >
                                                            ✖
                                                        </div>
                                                    </div>
                                                </template>
                                                <template #node-output="nodeProps">
                                                    <div class="custom-node">
                                                        <div class="handle">
                                                            <handle type="target" position="left" />
                                                        </div>
                                                        <div class="node-content">{{ nodeProps.data.label }}</div>
                                                        <div
                                                            class="node-delete-btn"
                                                            v-if="nodeProps.selected"
                                                            @click.stop="removeNode(nodeProps.id)"
                                                        >
                                                            ✖
                                                        </div>
                                                    </div>
                                                </template>
                                                <Background />
                                                <Controls />
                                            </VueFlow>
                                        </div>
                                    </div>
                                </div>
                            </a-col>
                            <a-col :span="24">
                                <div :class="['sop-modal', { 'readonly-wrapper': sopState.isDetail }]">
                                    <a-col :span="24" v-if="isShowForm">
                                        <a-form :model="selectedNode">
                                            <a-col :span="24">
                                                <a-form-item
                                                    label="SOP步骤"
                                                    name="sopStep"
                                                    :rules="sopState.sopFormRole.sopStep"
                                                >
                                                    <a-input
                                                        placeholder="请输入"
                                                        maxlength="100"
                                                        show-count
                                                        v-model:value="selectedNode.sopStep"
                                                    />
                                                </a-form-item>
                                            </a-col>
                                            <a-col :span="24">
                                                <a-form-item
                                                    label="解决角色"
                                                    name="solverRole"
                                                    :rules="sopState.sopFormRole.solverRole"
                                                >
                                                    <a-select
                                                        allow-clear
                                                        v-model:value="selectedNode.solverRole"
                                                        show-search
                                                        :options="sopState.solverList"
                                                        placeholder="请选择"
                                                        :field-names="{ label: 'label', value: 'value' }"
                                                        :filter-option="
                                                            (input, option) => {
                                                                return (
                                                                    option.label
                                                                        .toLowerCase()
                                                                        .indexOf(input.toLowerCase()) >= 0
                                                                )
                                                            }
                                                        "
                                                    />
                                                </a-form-item>
                                            </a-col>
                                        </a-form>
                                    </a-col>
                                    <a-col :span="24">
                                        <a-form-item label="完成标准">
                                            <QuillEditor
                                                v-model:content="sopState.sopModalForm.finishStandard"
                                                ref="finishQuillEditor"
                                                :options="editorOption"
                                                class="quill-editor"
                                            />
                                        </a-form-item>
                                    </a-col>
                                    <a-col :span="24">
                                        <a-form-item label="审核标准">
                                            <QuillEditor
                                                v-model:content="sopState.sopModalForm.assessorStandard"
                                                ref="assQuillEditor"
                                                :options="editorOption"
                                                class="quill-editor"
                                            />
                                        </a-form-item>
                                    </a-col>
                                    <div class="modal-title"></div>
                                    <a-col :span="24">
                                        <a-form-item label="附件模板">
                                            <a-upload
                                                v-model:file-list="sopState.sopModalForm.attachmentTemplateList"
                                                :multiple="true"
                                                :before-upload="handlerSopUpload"
                                                @remove="handlerSopRemove"
                                                accept=".xlsx, image/*,.docx,, .xls,.mp4"
                                            >
                                                <a-button type="primary">
                                                    <upload-outlined />
                                                    上传
                                                </a-button>
                                            </a-upload>
                                            <span>支持格式：.xlsx, image/*,.docx,, .xls,.mp4</span>
                                        </a-form-item>
                                    </a-col>
                                </div>
                            </a-col>
                        </a-row>
                    </div>
                </a-form>
            </a-modal>
            <!--          新建编辑工单SOP-->
            <div class="sop-body">
                <div class="sop-top-search">
                    <a-row :gutter="24">
                        <a-col :span="6">
                            <a-form-item label="SOP名称" :label-col="{ span: 5 }" :wrapper-col="{ span: 19 }">
                                <a-input v-model:value="sopState.filter.sopName" />
                            </a-form-item>
                        </a-col>
                        <a-col :span="6">
                            <a-form-item label="工单类型" :label-col="{ span: 5 }" :wrapper-col="{ span: 19 }">
                                <a-select
                                    allow-clear
                                    show-search
                                    v-model:value="sopState.filter.workType"
                                    :options="sopState.workTypeList"
                                    placeholder="请选择"
                                    :field-names="{ label: 'label', value: 'value' }"
                                    :filterOption="
                                        (input, option) => {
                                            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                                        }
                                    "
                                    @change="hanldeTypeChange(true)"
                                />
                            </a-form-item>
                        </a-col>
                        <a-col :span="6">
                            <a-form-item label="监控场景" :label-col="{ span: 5 }" :wrapper-col="{ span: 19 }">
                                <a-select
                                    allow-clear
                                    show-search
                                    v-model:value="sopState.filter.monitorScene"
                                    :options="sopState.monitorSceneList"
                                    placeholder="请选择"
                                    :field-names="{ label: 'label', value: 'value' }"
                                    :filterOption="
                                        (input, option) => {
                                            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                                        }
                                    "
                                    @focus="getMonitorSceneList(true)"
                                />
                            </a-form-item>
                        </a-col>
                        <a-col :span="6" style="display: flex; justify-content: end; align-items: start">
                            <a-space :size="16">
                                <a-button type="primary" @click="getSopData">查询</a-button>
                                <a-button type="primary" @click="resetSopData" ghost>重置</a-button>
                            </a-space>
                        </a-col>
                    </a-row>
                </div>
                <div class="sop-content">
                    <div class="sop-title">
                        <div class="sop-title-left"> 工单SOP列表 </div>
                        <a-button type="primary" ghost @click="handleAddSop">新建</a-button>
                    </div>
                    <div class="sop-table-box">
                        <a-table
                            :columns="sopState.columns"
                            :dataSource="sopState.dataSource"
                            :pagination="false"
                            :loading="sopState.tableLoading"
                            bordered
                            @resizeColumn="handleResizeColumn"
                            :showSorterTooltip="false"
                            ref="tableRef"
                            :scroll="{ y: 600 }"
                        >
                            <template #bodyCell="{ record, text, column, index }">
                                <template v-if="column.dataIndex === 'attachmentTemplateList'">
                                    <!--                                  预览/下载-->
                                    <div v-for="(item, index) in record.attachmentTemplateList" :key="index">
                                        <a-dropdown>
                                            <span
                                                style="
                                                    overflow: hidden;
                                                    white-space: nowrap;
                                                    text-overflow: ellipsis;
                                                    display: block;
                                                "
                                                :title="item.name"
                                            >
                                                {{ item.name }}
                                            </span>
                                            <template #overlay>
                                                <a-menu>
                                                    <!--                                                    <a-menu-item v-if="item.type.startsWith('image')">-->
                                                    <!--                                                        <a-button-->
                                                    <!--                                                            type="link"-->
                                                    <!--                                                            size="small"-->
                                                    <!--                                                            :disabled="!item.type.startsWith('image')"-->
                                                    <!--                                                            @click="viewImage(item.url)"-->
                                                    <!--                                                            >预览</a-button-->
                                                    <!--                                                        >-->
                                                    <!--                                                    </a-menu-item>-->
                                                    <a-menu-item>
                                                        <a-button
                                                            style="width: 100%"
                                                            type="link"
                                                            size="small"
                                                            @click="downLoadSopFile(item)"
                                                            >下载</a-button
                                                        >
                                                    </a-menu-item>
                                                </a-menu>
                                            </template>
                                        </a-dropdown>
                                    </div>
                                    <!--                                  预览/下载-->
                                </template>
                                <template v-if="column.dataIndex === 'opration'">
                                    <a-space>
                                        <a-button type="link" size="small" @click="handlerEditSop(record.sopId, false)"
                                            >编辑</a-button
                                        >
                                        <a-button type="link" size="small" @click="handleDetail(record.sopId)"
                                            >详情</a-button
                                        >
                                        <a-popconfirm
                                            title="确认删除吗?"
                                            ok-text="确定"
                                            cancel-text="取消"
                                            @confirm="handlerDeleteSop(record.sopId)"
                                            @cancel="cancel"
                                        >
                                            <a-button type="link" size="small">删除</a-button>
                                        </a-popconfirm>
                                    </a-space>
                                </template>
                            </template>
                        </a-table>
                    </div>
                </div>
            </div>
            <!-- end vid_q2pj193dfc 自定义模板_7xu4mdl9mj -->
            <!-- begin vid_3z78u6tjtg 图片_fw0b2rnibt -->
            <al-image
                v-if="false"
                class="vid_3z78u6tjtg"
                :emptyImgSrc="''"
                :src="vid_3z78u6tjtg_props.src"
            />
            <!-- end vid_3z78u6tjtg 图片_fw0b2rnibt -->
        </a-spin>
    </div>
</template>
<script setup lang="tsx">
    import {
        h,
        ref,
        reactive,
        toRaw,
        markRaw,
        watch,
        computed,
        onBeforeMount,
        onMounted,
        onBeforeUnmount,
        onUnmounted,
        onActivated,
        onDeactivated,
        nextTick
    } from 'vue'
    import { useRoute, useRouter } from 'vue-router'
    const router = useRouter()
    const route = useRoute()
    import { get, post, put, del, request, requestWithHeaders } from '@/utils/http/Axios'
    import {
        cloneDeep,
        localGet,
        merge,
        getDifferenceArray,
        traversalTreeStructure,
        checkCaseCondition,
        renderStringInTemplate,
        handleBreadcrumbJump,
        throttle,
        permission,
        getComponentParamsValue
    } from '@/utils'
    import { isNullOrEmpty, isEmptyArr, isNullOrUnDef, isContained, isArray, isEmpty, isObject } from '@/utils/is'
    import dayjs from 'dayjs'
    import { getPagePcChartsDataSourceConvert } from '@/utils/chart'
    import { message, notification, Modal } from 'ant-design-vue'
    import {
        propertyFormatByNumber,
        propertyFormatByPercentage,
        propertyFormatByDate,
        parseSubFromDataList
    } from '@/utils/format'
    import colorTheme from '@/design/custom-theme.json'
    /*** PUBLISH_EXEGESIS ***
 import UserTableRecords from '@/model/UserTableRecords';
 import { getAppBaseUrl } from '@/utils/index';
 import { sendToken } from "@/hooks/useIframeToken";
 import { useSystemStore } from "@/store/modules/system";
 const systemStore = useSystemStore();
 *** PUBLISH_EXEGESIS ***/

    // 获取传入的流程ID和组件参数
    const props = defineProps({
        'processInstanceId': {
            type: [String, Number, Boolean],
            default: undefined
        }
    })
    const immediateWatch = (source, callback) => {
        watch(source, callback, { deep: true, immediate: true })
    }

    /* -------------------------- 属性定义 -------------------------- */

    const setTimeOutList = []
    const pageLoading = reactive({
        loading: false,
        tip: undefined
    })
    const authInfo = localGet('app-user-store')
    const vid_3z78u6tjtg_imageUrl = 'http://10.40.0.33:9001/file-storage/487394680222738432-source.svg'

    const vid_3z78u6tjtg_props = reactive({
        src: vid_3z78u6tjtg_imageUrl
    })

    // 处理校验
    const setSubFormName = (parentInfo, name) => {
        return [parentInfo.parents, parentInfo.cycleIndex, name]
    }

    /* ========================== 属性定义 ========================== */

    /* -------------------------- 元素数据引用 -------------------------- */

    /* ========================== 元素数据引用 ========================== */

    /* -------------------------- 页面数据 -------------------------- */

    /* ========================== 页面数据 ========================== */

    /* -------------------------- 响应 -------------------------- */

    /* ========================== 响应 ========================== */

    /* -------------------------- 函数 -------------------------- */

    /* ========================== 函数 ========================== */

    /* -------------------------- 页面参数 -------------------------- */

    const pageParameter_state = reactive({
        'processInstanceId': props.processInstanceId ?? route.query.processInstanceId
    })

    /* ========================== 页面参数 ========================== */

    /* -------------------------- 组件事件处理方法 -------------------------- */

    // ---------------------------- 组件数据同步 ----------------------------

    // ============================ 组件数据同步 ============================

    /* ========================== 组件事件处理方法 ========================== */

    /* -------------------------- 生命周期 -------------------------- */
    // 用于参数变更后重新执行自定义指令
    const queryUpdateFlag = ref(0)
    onMounted(() => {})

    onUnmounted(() => {
        // 销毁定时器
        setTimeOutList.forEach(({ repeat, timer }) => {
            repeat ? clearInterval(timer) : clearTimeout(timer)
        })
    })

    /* ========================== 生命周期 ========================== */

    /* -------------------------- 数据关联处理 -------------------------- */

    /* -------------------------- 表单数据同步 -------------------------- */
    /* ========================== 表单数据同步 ========================== */

    /* ========================== 数据关联处理 ========================== */

    /* -------------------------- 不同组件自定义的处理逻辑 -------------------------- */

    /* ========================== 不同组件自定义的处理逻辑 ========================== */

    // ================================定制化逻辑========================================
    import { nextTick } from 'vue'
    import { QuillEditor, Quill } from '@vueup/vue-quill'
    import ImageUploader from 'quill-image-uploader'
    import '@vueup/vue-quill/dist/vue-quill.snow.css'
    import { BaseEdge, getSmoothStepPath, Handle, VueFlow } from '@vue-flow/core'
    import { Background } from '@vue-flow/background'
    import { Controls } from '@vue-flow/controls'
    const titleConfig = [
        { Choice: ' .ql-bold', title: '加粗' },
        { Choice: ' .ql-italic', title: '斜体' },
        { Choice: ' .ql-underline', title: '下划线' },
        { Choice: ' .ql-strike', title: '删除线' },
        { Choice: ' .ql-blockquote', title: '块引用' },
        { Choice: ' .ql-code-block', title: '插入代码' },
        { Choice: ' .ql-size', title: '字体大小' },
        { Choice: ' .ql-size', title: '字体大小' },
        { Choice: ' .ql-color', title: '字体颜色' },
        { Choice: ' .ql-background', title: '字体背景颜色' },
        { Choice: ' .ql-list[value="ordered"]', title: '有序列表' },
        { Choice: ' .ql-list[value="bullet"]', title: '无序列表' },
        { Choice: ' .ql-header[value="1"]', title: '1级标题' },
        { Choice: ' .ql-header[value="2"]', title: '2级标题' },
        { Choice: ' .ql-align', title: '对齐方式' },
        { Choice: ' .ql-clean', title: '清除字体格式' },
        { Choice: ' .ql-image', title: '图像' },
        { Choice: ' .ql-size .ql-picker-item[data-value="small"]', title: '小号' },
        { Choice: ' .ql-size .ql-picker-item[data-value="large"]', title: '大号' },
        { Choice: ' .ql-size .ql-picker-item[data-value="huge"]', title: '超大号' },
        { Choice: ' .ql-size .ql-picker-item:nth-child(2)', title: '标准' },
        { Choice: ' .ql-align .ql-picker-item:first-child', title: '居左对齐' },
        { Choice: ' .ql-align .ql-picker-item[data-value="center"]', title: '居中对齐' },
        { Choice: ' .ql-align .ql-picker-item[data-value="right"]', title: '居右对齐' },
        { Choice: ' .ql-align .ql-picker-item[data-value="justify"]', title: '两端对齐' }
    ]
    // 注册图片上传模块
    Quill.register('modules/imageUploader', ImageUploader)

    // 将本地文件转换为URL
    const getLocalImageURL = file => {
        return new Promise(resolve => {
            const reader = new FileReader()
            reader.onload = e => {
                resolve(e.target.result)
            }
            reader.readAsDataURL(file)
        })
    }

    // 表单数据

    // Quill 编辑器选项
    const editorOption = ref({
        modules: {
            // imageUploader: {
            //     upload: async file => {
            //         const localUrl = await getLocalImageURL(file)
            //         return localUrl // 返回本地图片的 URL 进行预览
            //     }
            // },
            toolbar: [
                ['bold', 'italic', 'underline', 'strike'], // 加粗，斜体，下划线，删除线
                // ['blockquote', 'code-block'], // 引用，代码块
                // [{ header: 1 }, { header: 2 }], // 标题1，标题2
                [{ list: 'ordered' }, { list: 'bullet' }], // 有序列表，无序列表
                [{ size: ['small', false, 'large', 'huge'] }], // 文字大小
                [{ color: [] }, { background: [] }], // 字体颜色，背景颜色
                [{ align: [] }], // 对齐
                ['clean'] // 清除格式
                // ['image'] // 链接，图片，视频
            ]
            // ImageUploader: imageHandler
        }
    })

    // 将 dataURL 转换为 File 对象的辅助函数
    const dataURLtoFile = (dataurl, filename) => {
        const arr = dataurl.split(',')
        const mime = arr[0].match(/:(.*?);/)[1]
        const bstr = atob(arr[1])
        let n = bstr.length
        const u8arr = new Uint8Array(n)
        while (n--) {
            u8arr[n] = bstr.charCodeAt(n)
        }
        return new File([u8arr], filename, { type: mime })
    }
    // 上传API
    const uploadFileAPI = async file => {
        const data = new FormData()
        data.append('file', file)

        return post('/file-storage/api/v1/file/upload', data)
            .then(({ data: { fullUrl } }) => {
                return { data: { fullUrl } }
            })
            .catch(error => {
                console.error('上传图片出错:', error)
                throw error // 抛出错误以供外部处理
            })
    }
    function initTitle() {
        // 选择所有的 ql-editor，并去除 placeholder
        document.querySelectorAll('.ql-editor').forEach(editor => {
            editor.dataset.placeholder = ''
        })

        // 遍历 titleConfig 并处理多个富文本
        for (let item of titleConfig) {
            document.querySelectorAll('.ql-toolbar' + item.Choice).forEach(tip => {
                tip.setAttribute('title', item.title)
            })
        }
    }
    //     富文本设置
    const sopState = reactive({
        filter: {
            sopName: undefined,
            workType: undefined,
            monitorScene: undefined
        },
        tableLoading: false,

        dataSource: [],
        // 工单类型list
        workTypeList: [],
        // 监控场景
        monitorSceneList: [],
        columns: [
            {
                title: 'SOP名称',
                dataIndex: 'sopName',
                align: 'center',
                width: 150,
                ellipsis: true,
                customCell: ({ rowSpan }) => {
                    return {
                        rowSpan: rowSpan
                    }
                }
            },
            {
                title: '工单类型',
                dataIndex: 'workType',
                ellipsis: true,
                width: 120,
                align: 'center',
                customCell: ({ rowSpan }) => {
                    return {
                        rowSpan: rowSpan
                    }
                }
            },
            {
                title: '监控场景',
                dataIndex: 'monitorSceneValue',
                ellipsis: true,
                width: 120,
                align: 'center',
                customCell: ({ rowSpan }) => {
                    return {
                        rowSpan: rowSpan
                    }
                }
            },
            {
                title: '所属业务',
                dataIndex: 'belongBusiness',
                ellipsis: true,
                width: 120,
                align: 'center',
                customCell: ({ rowSpan }) => {
                    return {
                        rowSpan: rowSpan
                    }
                }
            },
            {
                title: '附件模板',
                dataIndex: 'attachmentTemplateList',
                ellipsis: true,
                width: 120,
                align: 'center',
                customCell: ({ rowSpan }) => {
                    return {
                        rowSpan: rowSpan
                    }
                }
            },
            {
                title: '创建时间',
                dataIndex: 'createdTime',
                ellipsis: true,
                width: 120,
                align: 'center',
                customCell: ({ rowSpan }) => {
                    return {
                        rowSpan: rowSpan
                    }
                }
            },
            {
                title: '创建人',
                dataIndex: 'createdBy',
                ellipsis: true,
                width: 120,
                align: 'center',
                customCell: ({ rowSpan }) => {
                    return {
                        rowSpan: rowSpan
                    }
                }
            },
            {
                title: '操作',
                dataIndex: 'opration',
                ellipsis: true,
                width: 120,
                align: 'center',
                fixed: 'right',
                customCell: ({ rowSpan }) => {
                    return {
                        rowSpan: rowSpan
                    }
                }
            }
        ],
        sopModalForm: {
            sopName: undefined, //sop名称
            finishStandard: undefined, //完成标准
            assessorStandard: undefined, //审核标准
            workType: undefined, //工单类型
            monitorScene: undefined, //监控场景
            belongBusiness: '工单', //所属业务
            attachmentTemplateList: [], //附件名称,
            flowChart: {}
        },
        solverList: [], //解决角色选择
        sopModalVisible: false,
        isDetail: false,
        modalTitle: '新建工单SOP',
        okLoading: false,
        sopFormRole: {
            sopName: [{ required: true, message: '请输入SOP名称', trigger: 'blur' }],
            sopStep: [{ required: true, message: '请输入SOP步骤', trigger: 'blur' }],
            solverRole: [{ required: true, message: '请选择解决角色', trigger: 'blur' }],
            workType: [{ required: true, message: '请选择工单类型', trigger: 'blur' }],
            monitorScene: [{ required: true, message: '请选择监控场景', trigger: 'blur' }],
            attachmentTemplateList: [{ required: true, message: '上传附件', trigger: 'blur' }]
        }
    })
    onMounted(() => {
        getSopData()
        getWorkType()
        getSolverList()
    })

    // 获取列表
    function getSopData() {
        sopState.tableLoading = true
        post('/solution-ppm/api/v2/built-in-system-ppm/work_order_sop/search', sopState.filter)
            .then(data => {
                if (data.code !== 200) {
                    message.error(data.message)
                    return
                }
                sopState.dataSource = groupSopData(data.data)
            })
            .finally(() => {
                sopState.tableLoading = false
            })
    }
    // 获取工单类型字典值
    function getWorkType() {
        get('/solution-ppm/api/v2/built-in-system-ppm/work_order_sop/get_work_type_by_monitor_scene').then(data => {
            if (data.code !== 200) {
                message.error(data.message)
                return
            }
            sopState.workTypeList = data.data.map(item => {
                return {
                    label: item.itemName,
                    value: item.itemName
                }
            })
        })
    }
    // 获取监控场景list
    function getMonitorSceneList(flag) {
        console.log(flag)
        const params = flag ? sopState.filter.workType : sopState.sopModalForm.workType
        console.log(params, 'params')
        get(`/solution-ppm/api/v2/built-in-system-ppm/dictionary/search/${params}`).then(data => {
            if (data.code !== 200) {
                message.error(data.message)
                return
            }
            sopState.monitorSceneList = data.data.map(item => {
                return {
                    label: item.itemName,
                    value: item.itemCode
                }
            })
        })
    }
    // 解决角色list
    function getSolverList() {
        get('/solution-ppm/api/v2/built-in-system-ppm/work_order_sop/template_role').then(data => {
            if (data.code !== 200) {
                message.error(data.message)
                return
            }
            sopState.solverList = data.data
        })
    }
    const handleResizeColumn = (w, col, vid) => {
        col.width = w
    }
    // 打开图片的函数
    function viewImage(url) {
        window.open(url, '_blank') // 在新窗口打开图片
    }
    // 下载文件的函数
    function downLoadSopFile(item) {
        fetch(item.url)
            .then(response => response.blob())
            .then(blob => {
                const downloadLink = document.createElement('a')
                const url = URL.createObjectURL(blob)
                downloadLink.href = url
                downloadLink.setAttribute('download', item.name)
                downloadLink.style.display = 'none'
                document.body.appendChild(downloadLink)
                downloadLink.click()
                document.body.removeChild(downloadLink)

                // Release the object URL after the download
                URL.revokeObjectURL(url)
                pageLoading.loading = false
            })
            .catch(err => {
                console.error('Download failed', err)
            })
    }
    const isExpanded = ref(false) // 控制展开/折叠的状态
    // 上传文件
    // 父上传文件
    function handlerSopUpload(file) {
        const data = new FormData()
        data.append('file', file)

        post('/file-storage/api/v1/file/upload', data)
            .then(({ data: { fullUrl } }) => {
                if (data) {
                    // 新文件对象
                    const newFile = {
                        uid: file.uid,
                        lastModified: file.lastModified,
                        name: file.name,
                        size: file.size,
                        type: file.type,
                        status: 'done',
                        url: fullUrl
                    }

                    // 去重，保留一个副本
                    sopState.sopModalForm.attachmentTemplateList = [
                        // 保留已存在文件（去除重复）
                        ...sopState.sopModalForm.attachmentTemplateList.filter(
                            item => item.uid !== file.uid && item.name !== file.name
                        ),
                        // 添加新的文件
                        newFile
                    ]
                }
            })
            .catch(e => {
                message.error('上传失败' + e)
            })

        return false
    }
    // 父移除文件
    function handlerSopRemove(file) {
        sopState.sopModalForm.attachmentTemplateList = sopState.sopModalForm.attachmentTemplateList.filter(
            item => item.uid !== file.uid
        )
    }
    // 父上传文件
    function resetSopData() {
        sopState.filter = {
            sopName: undefined,
            type: undefined,
            monitorScene: undefined
        }
        getSopData()
    }
    //     新建sop
    function handleAddSop() {
        sopState.modalTitle = '新建SOP'
        currentSopData.value = {}
        sopState.monitorSceneList = []
        sopState.sopModalVisible = true
        sopState.isDetail = false
        nextTick(() => {
            initTitle()
        })
        nodes.value = [
            {
                id: 'start-1',
                type: 'start',
                data: { label: '开始' },
                position: { x: 50, y: 100 },
                sourcePosition: Position.Right
            }
        ]
    }
    function resetNodes() {
        nodes.value = [
            {
                id: 'start-1',
                type: 'start',
                data: { label: '开始' },
                position: { x: 50, y: 100 },
                sourcePosition: Position.Right
            }
        ]
        edges.value = []
    }

    const sopFormRef = ref(null)
    const finishQuillEditor = ref(null)
    const assQuillEditor = ref(null)
    const currentSopData = ref({})
    function groupSopData(data) {
        let currentSopName = ''

        // 遍历数据，计算每一行的 rowSpan
        return data.map((item, index) => {
            if (currentSopName !== item.sopName) {
                // 更新当前的标识符
                currentSopName = item.sopName

                // 计算这个组的 rowSpan
                let rowSpan = 0
                for (let i = index; i < data.length; i++) {
                    const currentItem = data[i]
                    if (currentSopName === currentItem.sopName) {
                        rowSpan += 1
                    } else {
                        break
                    }
                }
                item.rowSpan = rowSpan
            } else {
                item.rowSpan = 0 // 如果是相同的组，重置 rowSpan
            }

            return item
        })
    }
    //     保存sop
    function confirmSop() {
        sopFormRef.value.validate().then(async () => {
            sopState.okLoading = true
            const finishDeltaContent = finishQuillEditor.value.getContents()
            const assDeltaContent = assQuillEditor.value.getContents()

            const imageUploadPromises = []

            finishDeltaContent.ops.forEach(op => {
                if (op.insert && op.insert.image && op.insert.image.startsWith('data:')) {
                    const localSrc = op.insert.image

                    // 如果是 base64 图片，上传并替换为 URL
                    const file = dataURLtoFile(localSrc, 'image.png') // 将 base64 转换为 File 对象

                    const uploadPromise = uploadFileAPI(file).then(({ data: { fullUrl } }) => {
                        // 替换 op 中的 base64 URL 为服务器返回的 URL
                        op.insert.image = fullUrl
                    })

                    imageUploadPromises.push(uploadPromise)
                }
            })
            assDeltaContent.ops.forEach(op => {
                if (op.insert && op.insert.image && op.insert.image.startsWith('data:')) {
                    const localSrc = op.insert.image

                    // 如果是 base64 图片，上传并替换为 URL
                    const file = dataURLtoFile(localSrc, 'image.png') // 将 base64 转换为 File 对象

                    const uploadPromise = uploadFileAPI(file).then(({ data: { fullUrl } }) => {
                        // 替换 op 中的 base64 URL 为服务器返回的 URL
                        op.insert.image = fullUrl
                    })

                    imageUploadPromises.push(uploadPromise)
                }
            })
            try {
                await Promise.all(imageUploadPromises)
                if (currentSopData.value.sopId) {
                    // 编辑保存逻辑
                    const params = { ...sopState.sopModalForm }
                    params.finishStandard = JSON.stringify(finishDeltaContent)
                    params.assessorStandard = JSON.stringify(assDeltaContent)
                    params.flowChart = JSON.stringify({
                        lineList: edges.value,
                        chartList: nodes.value
                    })
                    put('/solution-ppm/api/v2/built-in-system-ppm/work_order_sop', params)
                        .then(data => {
                            if (data.code !== 200) {
                                message.error(data.message)
                                return
                            }
                            message.success(data.message)
                            sopState.sopModalVisible = false
                            resetForm()
                            getSopData()
                        })
                        .finally(() => {
                            sopState.monitorSceneList = []
                            sopState.okLoading = false
                        })
                } else {
                    // 新建保存逻辑
                    const params = { ...sopState.sopModalForm }
                    params.finishStandard = JSON.stringify(finishDeltaContent)
                    params.assessorStandard = JSON.stringify(assDeltaContent)
                    params.flowChart = JSON.stringify({
                        lineList: edges.value,
                        chartList: nodes.value
                    })
                    post('/solution-ppm/api/v2/built-in-system-ppm/work_order_sop', params)
                        .then(data => {
                            if (data.code !== 200) {
                                message.error(data.message)
                                return
                            }
                            message.success(data.message)
                            sopState.sopModalVisible = false
                            resetForm()
                            getSopData()
                        })
                        .finally(() => {
                            sopState.monitorSceneList = []
                            sopState.okLoading = false
                        })
                }
            } catch (error) {
                sopState.okLoading = false
                console.error('图片上传失败', error)
                return // 终止提交
            }
        })
    }
    // 编辑反显
    function handlerEditSop(id, isDetail) {
        get(`/solution-ppm/api/v2/built-in-system-ppm/work_order_sop/${id}`).then(async data => {
            if (data.code !== 200) {
                message.error(data.message)
                return
            }
            sopState.sopModalVisible = true
            sopState.isDetail = isDetail ? true : false
            currentSopData.value = data.data
            sopState.modalTitle = isDetail ? '详情' : '编辑SOP'

            nextTick(() => {
                Object.assign(sopState.sopModalForm, data.data)
                console.log(data.data.flowChart)
                if (!data.data.flowChart) {
                    nodes.value = [
                        {
                            id: 'start-1',
                            type: 'start',
                            data: { label: '开始' },
                            position: { x: 50, y: 100 },
                            sourcePosition: Position.Right
                        }
                    ]
                } else {
                    nodes.value = JSON.parse(data.data.flowChart).chartList
                }

                edges.value = JSON.parse(data.data.flowChart).lineList
                getMonitorSceneList()
                let assessorContent = data.data.assessorStandard
                let finishContent = data.data.finishStandard

                try {
                    assessorContent = assessorContent ? JSON.parse(assessorContent) : { ops: [] }
                } catch (error) {
                    console.warn('assessorStandard 解析失败:', error)
                    assessorContent = { ops: [] }
                }

                try {
                    finishContent = finishContent ? JSON.parse(finishContent) : { ops: [] }
                } catch (error) {
                    console.warn('finishStandard 解析失败:', error)
                    finishContent = { ops: [] }
                }

                try {
                    assQuillEditor.value?.setContents(assessorContent)
                } catch (error) {
                    console.error('assQuillEditor 反显失败:', error)
                }

                try {
                    finishQuillEditor.value?.setContents(finishContent)
                } catch (error) {
                    console.error('finishQuillEditor 反显失败:', error)
                }
            })
        })
    }
    // 详情反显
    function handleDetail(id) {
        handlerEditSop(id, true)
        sopState.isDetail = true
        sopState.modalTitle = '详情'
    }
    // 删除sop
    function handlerDeleteSop(id) {
        del(`/solution-ppm/api/v2/built-in-system-ppm/work_order_sop/${id}`).then(data => {
            if (data.code !== 200) {
                message.error(data.message)
                return
            }
            message.success(data.message)
            getSopData()
        })
    }
    //     取消弹窗
    function cancelSop() {
        assQuillEditor.value?.setContents('')
        finishQuillEditor.value?.setContents('')
        sopState.sopModalVisible = false
        resetForm()
    }
    function resetForm() {
        sopState.sopModalForm = {
            sopName: undefined, //sop名称
            finishStandard: undefined, //完成标准
            assessorStandard: undefined, //审核标准
            workType: undefined, //工单类型
            monitorScene: undefined, //监控场景
            belongBusiness: '工单', //所属业务
            attachmentTemplateList: [] //附件名称
        }
        sopState.monitorSceneList = []
    }
    //     类型改变清空监控
    function hanldeTypeChange(flag) {
        if (flag) {
            sopState.filter.monitorScene = undefined
        } else {
            sopState.sopModalForm.monitorScene = undefined
        }
    }
    //     拖拉拽
    // 定制化代码
    import { VueFlow, useVueFlow, Handle, Panel } from '@vue-flow/core'
    import { Background } from '@vue-flow/background'
    import { Controls } from '@vue-flow/controls'
    import '@vue-flow/core/dist/style.css'
    import '@vue-flow/core/dist/theme-default.css'
    import { Position } from '@vue-flow/core'
    // 确保从正确路径导入 BaseEdge
    import { BaseEdge } from '@vue-flow/core' // 注意：不是 '@vue-flow/additional-components'
    // import CustomEdge from './CustomEdge.vue'
    import { getStraightPath, getBezierPath, getSmoothStepPath } from '@vue-flow/core'
    // import { backgroundColor } from 'html2canvas/dist/types/css/property-descriptors/background-color'
    // 🟢 1. 预配置节点列表
    const presetNodes = [
        // { id: '1', type: 'start', targetPosition: Position.Bottom, data: { label: '开始' }, isStart: true },
        { id: '2', type: 'input', sourcePosition: Position.Bottom, data: { label: '步骤' } },
        { id: '4', type: 'output', targetPosition: Position.Top, data: { label: '结束' } }
    ]

    // 🟢 2. 画布数据
    const nodes = ref([])
    const edges = ref([])

    // 🟢 3. 节点配置弹窗
    const selectedNode = ref({})
    const isShowForm = ref(false)

    // 节点点击 - 弹窗
    const onNodeClick = (event, node) => {
        console.log(event)
        if (event.node.type === 'input') {
            selectedNode.value = event.node.data
            isShowForm.value = true
        } else {
            selectedNode.value = {}
            isShowForm.value = false
        }
    }

    // 拖拽相关
    const onDragStart = (event, node) => {
        event.dataTransfer.setData('application/vueflow', JSON.stringify(node))
        event.dataTransfer.effectAllowed = 'move'
    }

    const onDragOver = event => {
        event.preventDefault()
        event.dataTransfer.dropEffect = 'move'
    }

    const onDrop = event => {
        event.preventDefault()
        const nodeData = JSON.parse(event.dataTransfer.getData('application/vueflow'))
        const bounds = event.currentTarget.getBoundingClientRect()
        const position = { x: event.clientX - bounds.left, y: event.clientY - bounds.top }

        const newNode = {
            id: `${nodeData.type}-${Date.now().toString(36)}`,
            type: nodeData.type,
            isStart: nodeData.isStart,
            position,
            data: { label: nodeData.data.label, sopStep: undefined, solverRole: undefined },
            targetPosition: nodeData.targetPosition,
            sourcePosition: nodeData.sourcePosition
        }
        nodes.value.push(newNode)
    }

    // 弹窗确认/取消
    const onModalOk = () => (isModalVisible.value = false)
    const onModalCancel = () => (isModalVisible.value = false)

    // // 节点删除方法
    function removeNode(id) {
        nodes.value = nodes.value.filter(node => node.id !== id)
        // 同时删除相关连线
        edges.value = edges.value.filter(edge => edge.source !== id && edge.target !== id)
    }

    // 连接线
    const { addEdges } = useVueFlow()
    const onConnect = params => {
        console.log('params', params) // 确保打印出连线参数
        // 确保每个连线数据都符合 VueFlow 的要求
        const newEdge = {
            ...params,
            // 确保路径、样式等数据正确,
            type: 'custom'
        }
        edges.value = [...edges.value, newEdge] // 手动更新 edges 数据
    }

    // 删除连线
    function removeEdge(id) {
        edges.value = edges.value.filter(edge => edge.id !== id)
    }

    // 保存
    const onSave = () => {
        console.log(nodes.value)
    }
    function getBacColor(props) {
        console.log(props, 'asdasdsdsadasds')
        if (!props.data.sopStep || !props.data.solverRole) {
            return '1px soild red'
        } else {
            return '1px soild #e5e7eb'
        }
    }
    function getFontColor(props) {
        // if (!props.data.sopStep || !props.data.solverRole) {
        //     return '#FFFFFF'
        // } else {
        //     return '#262626'
        // }
    }
    watch(
        () => [selectedNode.value.sopStep, selectedNode.value.solverRole],
        () => {
            const index = nodes.value.findIndex(node => node.id === selectedNode.value.id)
            if (index !== -1) {
                nodes.value[index].data.sopStep = selectedNode.value.sopStep
                nodes.value[index].data.solverRole = selectedNode.value.solverRole
            }
        },
        { deep: true }
    )
</script>
<style lang="less" scoped>
    .page_root {
        width: 100%;
        height: 100%;
        overflow: auto;
    }
    .page_root :deep(.@{ant-prefix}-spin-nested-loading),
    .page_root :deep(.@{ant-prefix}-spin-container) {
        height: 100%;
    }
    .page_root :deep(.@{ant-prefix}-spin-container .@{ant-prefix}-table-wrapper) {
        display: flex;
        flex-flow: column nowrap;
        justify-content: flex-start;
    }
    .page_root :deep(.@{ant-prefix}-table) {
        flex: auto;
        overflow: auto;
    }
    .table-moreBtn_down {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        width: 100px;
    }

    .table-moreBtn_menu :deep(.table-moreBtn_menu-item):hover {
        background: #ebf1ff;
    }

    /*  表格树结构样式处理，展开按钮层级增加*/
    :deep(.@{ant-prefix}-table-row-expand-icon) {
        z-index: 1;
    }
    /*  表格树结构 由于每个表格项采用自定义组件，使用绝对定位布局，开启树结构导致显示异常，暂时使用层级定位位置 */
    :deep(.@{ant-prefix}-table-row-level-1 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
        left: 15px !important;
        width: calc(100% - 15px);
    }
    :deep(.@{ant-prefix}-table-row-level-2 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
        left: 30px !important;
        width: calc(100% - 30px);
    }
    :deep(.@{ant-prefix}-table-row-level-3 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
        left: 45px !important;
        width: calc(100% - 45px);
    }
    :deep(.@{ant-prefix}-table-row-level-4 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
        left: 60px !important;
        width: calc(100% - 60px);
    }

    /* 穿梭框改变默认样式，为根据组件样式控制左右栏宽度。 */
    :deep(.@{ant-prefix}-transfer-list) {
        height: 100%;
        width: 50%;
    }
    /*工单sop*/
    .sop-body {
        overflow: hidden;
        height: 100%;
        .sop-top-search {
            height: 64px;
            background-color: #fff;
            padding: 16px 24px;
            border-radius: 2px;
            margin-bottom: 16px;
        }
        .sop-content {
            height: 100%;
            padding: 16px 24px;
            background-color: #fff;
            .sop-title {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 16px;
                .sop-title-left {
                    font-size: 16px;
                    color: #262626;
                    letter-spacing: 0;
                    font-weight: 600;
                    line-height: 16px;
                    padding-left: 8px;
                    border-left: 4px solid #3875ff;
                    height: 16px;
                }
            }
            .sop-table-box {
                padding-bottom: 24px;
                border-radius: 2px;
                background-color: #fff;
            }
            .sop-table-box:deep(.@{ant-prefix}-table-thead) > tr > th {
                background: #3875ff;
                color: #fff;
                font-weight: 600;
                padding: 8px 16px;
                border-right: 1px solid #d9d9d9 !important;
                position: relative;
            }
            .sop-table-box :deep(.@{ant-prefix}-table-tbody) tr > td {
                font-size: 14px;
                font-family: arial, helvetica, 'microsoft yahei';
                font-weight: 400;
                color: #262626;
                border-bottom: 1px solid #d9d9d9;
                padding: 8px 16px;
                word-break: break-all;
            }
        }
    }
    .sop-modal:deep(.ant3-form-item-label) {
        width: 90px;
    }
    :deep(.ql-editor) {
        min-height: 90px;
        background: #ffffff;
    }
    /*工单sop*/
    .modal-sop-title {
        height: 16px;
        line-height: 16px;
        padding-left: 8px;
        border-left: 4px solid #3875ff;
        font-size: 16px;
        color: #262626;
        letter-spacing: 0;
        font-weight: 600;
        margin-bottom: 16px;
        display: flex;
        justify-content: space-between;
    }
    /* 全局容器样式 */
    .flow-content {
        display: flex;
        flex-direction: column;
        height: 500px;
        overflow: auto;
        background-color: #f5f7fa;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
        margin-bottom: 16px;
    }

    /* 顶部操作栏 */
    .flow-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 12px 24px;
        background-color: #fff;
        border-bottom: 1px solid #e8e8e8;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
    }

    .flow-header-actions {
        display: flex;
        gap: 12px;
    }

    /* 主体容器 */
    .flow-container {
        display: flex;
        flex: 1;
        overflow: hidden;
    }

    /* 左侧边栏 */
    .sidebar {
        width: 100px;
        padding: 16px;
        background-color: #fff;
        border-right: 1px solid #e8e8e8;
        overflow-y: auto;
        text-align: center;
    }

    .sidebar h3 {
        margin: 0 0 16px 0;
        padding-bottom: 8px;
        color: #333;
        font-size: 16px;
        font-weight: 500;
        border-bottom: 1px solid #f0f0f0;
    }

    .draggable-node {
        padding: 10px 12px;
        margin-bottom: 8px;
        background-color: #f8f9fa;
        border: 1px solid #e9ecef;
        border-radius: 6px;
        color: #495057;
        font-size: 14px;
        cursor: grab;
        transition: all 0.2s ease;
    }

    .draggable-node:hover {
        background-color: #e9ecef;
        border-color: #dee2e6;
    }

    /* 画布区域 */
    .flow-area {
        flex: 1;
        position: relative;
        background-color: #f8f9fa;
        overflow: hidden;
    }
    /* 节点样式 */
    .custom-node {
        position: relative;
        height: 50px;
        border-radius: 8px;
        //box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        font-size: 14px;
        color: #fff;
        border: 2px solid #e5e7eb;
    }
    .node-content {
        position: absolute;
        bottom: -25px;
    }
    /* 不同类型节点颜色 */
    .vue-flow__node-start .custom-node {
        background-color: #fff;
        border: 2px solid rgba(114, 228, 204, 1);
        color: #262626;
        width: 50px;
        border-radius: 50%;
    }

    .vue-flow__node-input .custom-node {
        background-color: #ffffff;

        color: #262626;
    }

    .vue-flow__node-output .custom-node {
        background-color: #fff;
        border: 2px solid rgba(254, 112, 112, 1);
        color: #262626;
        width: 50px;
        border-radius: 50%;
    }
    :deep(.vue-flow__node-output) {
        padding: 0;
        width: unset;
        border: none;
        border-radius: 50%;
    }
    :deep(.vue-flow__node-output.selected) {
        border: none;
    }
    .sop-modal :deep(.vue-flow__node-input) {
        padding: 0;
        border: none;
    }
    .sop-modal :deep(.vue-flow__node-input.selected) {
        border: none;
    }
    /* 节点选中状态 */
    .vue-flow__node.selected .custom-node {
        border-color: #fff;
        box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.5);
    }

    /* 节点删除按钮 */
    .node-delete-btn {
        position: absolute;
        top: -8px;
        right: -8px;
        width: 20px;
        height: 20px;
        background: #ff4d4f;
        color: white;
        border-radius: 50%;
        text-align: center;
        line-height: 20px;
        font-size: 12px;
        cursor: pointer;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
        opacity: 1;
        transition: opacity 0.2s ease;
    }
    /* 节点新增下一个按钮 */
    .add-next-btn {
        position: absolute;
        top: -15px;
        right: 50px;
        width: 20px;
        height: 20px;
        background: #ff4d4f;
        color: white;
        border-radius: 50%;
        text-align: center;
        line-height: 20px;
        font-size: 12px;
        cursor: pointer;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
        opacity: 1;
        transition: opacity 0.2s ease;
    }
    .node-add-btn {
        position: absolute;
        top: -8px;
        right: -8px;
        width: 20px;
        height: 20px;
        background: #ff4d4f;
        color: white;
        border-radius: 50%;
        text-align: center;
        line-height: 20px;
        font-size: 12px;
        cursor: pointer;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
        opacity: 1;
        transition: opacity 0.2s ease;
    }

    .custom-node:hover .node-delete-btn {
        opacity: 1;
    }

    .node-delete-btn:hover {
        background: #ff7875;
    }

    /* 连线样式 */
    .vue-flow__edge-path {
        stroke: #888;
        stroke-width: 2;
    }

    .vue-flow__edge.selected .vue-flow__edge-path {
        stroke: #1890ff;
        stroke-width: 3;
    }

    /* 连线删除按钮 */
    .edge-delete-fo {
        pointer-events: all;
    }

    .edge-delete-btn {
        width: 20px;
        height: 20px;
        background: #ff4d4f;
        color: white;
        border-radius: 50%;
        text-align: center;
        line-height: 20px;
        font-size: 12px;
        cursor: pointer;
        box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
        opacity: 1;
        transition: opacity 0.2s ease;
    }

    .edge-delete-btn:hover {
        background: #ff7875;
    }

    /* 控制面板样式 */
    .vue-flow__controls {
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        border-radius: 6px;
        overflow: hidden;
    }

    .vue-flow__controls-button {
        background-color: #fff;
        border-bottom: 1px solid #f0f0f0;
        transition: background-color 0.2s ease;
    }

    .vue-flow__controls-button:hover {
        background-color: #f5f5f5;
    }

    .vue-flow__controls-button svg {
        fill: #595959;
    }

    /* 背景网格 */
    .vue-flow__background {
        background-color: #f8f9fa;
        background-image: linear-gradient(#e9ecef 1px, transparent 1px),
            linear-gradient(90deg, #e9ecef 1px, transparent 1px);
        background-size: 20px 20px;
    }

    /* 模态框样式 */
    .ant-modal-header {
        border-bottom: 1px solid #f0f0f0;
    }

    .ant-modal-title {
        color: #333;
        font-weight: 500;
    }

    .ant-form-item-label > label {
        color: #595959;
    }
    .readonly-wrapper {
        pointer-events: none;
        opacity: 0.85; /* 可选，加点透明度提示是只读 */
        cursor: not-allowed;
    }
    .node-line {
        width: 150px;
        //position: absolute;
        //bottom: -25px;
        //overflow: hidden;
        //text-overflow: ellipsis;
        //white-space: nowrap;
    }
</style>
